package tianrun.ziguan.api.calculate.config.service.common.edit;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.shiro.util.Assert;
import org.springframework.stereotype.Service;
import tianrun.ziguan.api.calculate.config.dto.request.common.DeleteBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.AddOrEditBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.add.edit.SaveBody;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.FilterField;
import tianrun.ziguan.api.calculate.config.dto.request.common.list.ListBody;
import tianrun.ziguan.api.calculate.config.entity.TCalLabelConfig;
import tianrun.ziguan.api.calculate.config.entity.TIndicatorsInstance;
import tianrun.ziguan.api.calculate.config.entity.TIndicatorsModule;
import tianrun.ziguan.api.calculate.config.service.cal.label.ITCalLabelConfigService;
import tianrun.ziguan.api.calculate.config.service.common.CommonService;
import tianrun.ziguan.api.calculate.config.service.expression.ITExpressionService;
import tianrun.ziguan.api.calculate.config.service.ge.TGeTableConfigService;
import tianrun.ziguan.api.calculate.config.service.template.instance.ITIndicatorsInstanceService;
import tianrun.ziguan.api.calculate.config.service.template.module.ITIndicatorsModuleService;
import tianrun.ziguan.api.calculate.config.utils.MapUtil;
import tianrun.ziguan.api.calculate.config.utils.StringUtil;
import tianrun.ziguan.api.common.util.ListUtil;
import tianrun.ziguan.api.common.util.SpringContextUtil;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class IndicatorInstanceDealService extends AbstractDealService {

    private final ITIndicatorsInstanceService indicatorsInstanceService;
    private final ITCalLabelConfigService calLabelConfigService;
    private final ITExpressionService expressionService;
    private final ITIndicatorsModuleService indicatorsModuleService;

    public IndicatorInstanceDealService(TGeTableConfigService tableConfigService,
                                        ITIndicatorsInstanceService indicatorsInstanceService,
                                        ITCalLabelConfigService calLabelConfigService, ITExpressionService expressionService, ITIndicatorsModuleService indicatorsModuleService) {
        super(tableConfigService);
        this.indicatorsInstanceService = indicatorsInstanceService;
        this.calLabelConfigService = calLabelConfigService;
        this.expressionService = expressionService;
        this.indicatorsModuleService = indicatorsModuleService;
    }

    @Override
    public void before(SaveBody body) {
        String calLabel = MapUtil.getString(body.getData(), "cal_label");
        if (StringUtil.isEmpty(calLabel)) return;
        Long templateId = MapUtil.getLong(body.getRawData(), "template_id");
        Assert.isTrue(null != templateId, "请传入template_id！");

        List<TIndicatorsModule> moduleByTemplate = indicatorsModuleService.getByTemplate(Sets.newHashSet(templateId));
        Assert.isTrue(!moduleByTemplate.isEmpty(), "未找到组件！");
        LambdaQueryWrapper<TIndicatorsInstance> wrapper = Wrappers.<TIndicatorsInstance>lambdaQuery()
                .eq(TIndicatorsInstance::getCalLabel, calLabel)
                .in(TIndicatorsInstance::getModuleId, moduleByTemplate.stream().map(TIndicatorsModule::getId).collect(Collectors.toSet()));
        if (Objects.equals(body.getOption(), SaveBody.Option.EDIT)) {
            wrapper.ne(TIndicatorsInstance::getId, body.getId());
        }
        int count = indicatorsInstanceService.count(wrapper);
        Assert.isTrue(count == 0, calLabel + "已存在！");

        //判断是否有自己算自己的情况
        List<JSONObject> configDatas = MapUtil.getList(body.getRawData(), "config_data", JSONObject.class);
        boolean match = configDatas.stream().anyMatch(o -> Objects.equals(o.getString("source"), "self") && Objects.equals(o.getString("label"), calLabel));
        Assert.isTrue(!match, "self中不允许引用要计算的点位："+calLabel);
    }


    @Override
    public String tableName() {
        return "t_indicators_instance";
    }

    @Override
    public void after(SaveBody body) {
        //删除无用的label_config
        List<JSONObject> data = MapUtil.getList(body.getRawData(), "config_data", JSONObject.class);
        Set<Long> ids = data.stream().map(jsonObject -> jsonObject.getLong("id")).filter(Objects::nonNull).collect(Collectors.toSet());
        LambdaQueryWrapper<TCalLabelConfig> wrapper = Wrappers.<TCalLabelConfig>lambdaQuery()
                .eq(TCalLabelConfig::getRelationId, body.getId())
                .eq(TCalLabelConfig::getRelationTable, tableName());
        if (!ListUtil.isEmpty(data)) {
            wrapper.notIn(!ids.isEmpty(), TCalLabelConfig::getId, ids);
        }
        calLabelConfigService.remove(wrapper);

        addChildData(body, "config_data", "t_cal_label_config", true);
        addChildData(body, "expression_data", "t_expression", false);
    }

    private Set<Long> addChildData(SaveBody body, String filed, String tableName, boolean isList) {
        CommonService commonService = SpringContextUtil.getBean(CommonService.class);

        if (!isList) {
            JSONObject jsonObject = MapUtil.getObject(body.getRawData(), filed, JSONObject.class);
            Object o = commonService.addOrEdit(getAddOrEditBody(body, tableName, jsonObject));
            return Sets.newHashSet(Long.parseLong(o.toString()));
        }
        List<JSONObject> data = MapUtil.getList(body.getRawData(), filed, JSONObject.class);
        if (ListUtil.isEmpty(data)) return new HashSet<>();
        StringBuilder sb = new StringBuilder();
        Set<Long> ids = data.stream().map(jsonObject -> getAddOrEditBody(body, tableName, jsonObject))
                .map(addOrEditBody -> {
                    try {
                        return Long.parseLong(commonService.addOrEdit(addOrEditBody).toString());
                    } catch (IllegalArgumentException e) {
                        sb.append(e.getMessage()).append(StringPool.NEWLINE);
                        return null;
                    }
                }).filter(Objects::nonNull).collect(Collectors.toSet());
        String message = sb.toString();
        Assert.isTrue(message.isEmpty(), message.trim());
        return ids;
    }

    private AddOrEditBody getAddOrEditBody(SaveBody body, String tableName, JSONObject jsonObject) {
        jsonObject.put("relation_id", body.getId());
        jsonObject.put("relation_table", tableName());
        jsonObject.put("cal_label", MapUtil.getString(body.getData(), "cal_label"));

        AddOrEditBody addOrEditBody = new AddOrEditBody();
        addOrEditBody.setTableName(tableName);
        addOrEditBody.setData(jsonObject);
        return addOrEditBody;
    }

    @Override
    public void delBefore(DeleteBody body) {

    }

    @Override
    public void listAfter(List<Map<String, Object>> result) {
        CommonService commonService = SpringContextUtil.getBean(CommonService.class);

        for (Map<String, Object> bean : result) {
            Object id = bean.get("id");
            ListBody listBody = new ListBody();
            FilterField isValidFilterField = new FilterField("is_valid", FilterField.FilterTypeEnum.equal, "1", FilterField.ConditionalSplicing.and, null);
            FilterField relationIdFilterField = new FilterField("relation_id", FilterField.FilterTypeEnum.equal, id, FilterField.ConditionalSplicing.and, null);
            listBody.setFilterFields(Lists.newArrayList(isValidFilterField, relationIdFilterField));
            listBody.setTableName("t_cal_label_config");
            bean.put("config_data", commonService.list(listBody));

            listBody.setTableName("t_expression");
            bean.put("expression_data", commonService.list(listBody).stream().findFirst().orElse(Maps.newHashMap()));
        }
    }

    @Override
    public void delAfter(DeleteBody body) {

    }

    @Override
    public void infoAfter(Map<String, Object> bean) {

    }


}
